// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: xguo

#pragma once

#include <memory>
#include <string>
#include <string_view>

#include "coin2/exchange/base/api_base/fix/fix_trade_client_base.h"
#include "coin2/exchange/base/api_base/fix/fix_trade_client_interface.h"
#include "coin2/exchange/base/market/market_exchange_api.h"
#include "coin2/exchange/deribit_base/api_order/fix/fix_message_builder.h"

namespace FIX {
class Application;
}

namespace coin2::exchange::deribit::api_order::fix {

constexpr auto ACCESS_KEY = "AccessKey";
constexpr auto SECRET_KEY = "SecretKey";
constexpr auto CANCEL_ORDERS_ON_DISCONNECT = "CancelOrdersOnDisconnect";

/********************************************************************************
 * class FixTradeClient                                                      *
 ********************************************************************************/
using IFixTradeClient = coin2::exchange::base::api_base::fix::IFixTradeClient;
template <class FixTradeClient>
using FixTradeClientBase = coin2::exchange::base::api_base::fix::FixTradeClientBase<FixTradeClient>;

class FixTradeClient : public FIX::Application,
                       public FixTradeClientBase<FixTradeClient>,
                       public IFixTradeClient {
 public:
  using RequestType = FixTradeClientBase<FixTradeClient>::RequestType;
  using ResponseType = FixTradeClientBase<FixTradeClient>::ResponseType;
  using RequestTypePtr = FixTradeClientBase<FixTradeClient>::RequestTypePtr;
  using SubscriberCB = FixTradeClientBase<FixTradeClient>::SubscriberCB;
  using Executor = FixTradeClientBase<FixTradeClient>::Executor;
  using Processor = FixTradeClientBase<FixTradeClient>::Processor;
  using OrderExecution = FixTradeClientBase<FixTradeClient>::OrderExecution;
  using FixMessageBuilder = coin2::exchange::deribit::api_order::fix::FixMessageBuilder;
  using MarketExchangeApi = coin2::exchange::base::market::MarketExchangeApi;
  using GetProductHolderFunc = IFixTradeClient::GetProductHolderFunc;

 public:
  explicit FixTradeClient(std::string_view, const MarketExchangeApi& mea);
  //
  // implement interface FIX::Application
  //
  void onCreate(const FIX::SessionID&) override;
  void onLogon(const FIX::SessionID&) override;
  void onLogout(const FIX::SessionID&) override;
  void toAdmin(FIX::Message&, const FIX::SessionID&) override;
  void toApp(FIX::Message&, const FIX::SessionID&) EXCEPT(FIX::DoNotSend) override;
  void fromAdmin(const FIX::Message&, const FIX::SessionID&)
      EXCEPT(FIX::FieldNotFound, FIX::IncorrectDataFormat, FIX::IncorrectTagValue, FIX::RejectLogon)
          override;
  void fromApp(const FIX::Message&, const FIX::SessionID&) EXCEPT(
      FIX::FieldNotFound,
      FIX::IncorrectDataFormat,
      FIX::IncorrectTagValue,
      FIX::UnsupportedMessageType) override;
  //
  // impliment interface IFixTradeClient
  //
  void Start() override;
  void Stop() override;
  void OnTimer(const boost::system::error_code& ec);
  bool IsAvailable() const override;
  void SetExecutor(Executor*) override;
  void SetProcessor(Processor*) override;
  void SetGetProductHolder(GetProductHolderFunc get_product_holder) override {
    get_product_holder_ = get_product_holder;
  }
  void AddSubscriber(SubscriberCB) override;
  void PlaceOrder(const OrderExecution&) override;
  void CancelOrder(const OrderExecution&) override;
  void QueryOrder(const OrderExecution&) override{};

  void AssignSubSystemAuthKey(FIX::SessionSettings*);
  void ValidateSubSystemAuthKey(const AuthKey&) const;
  auto GetProductHolder() const { return get_product_holder_; }

  auto GetContractValue(
      const std::string& native_product,
      int64_t ts,
      const MarketExchangeApi& mea,
      GetProductHolderFunc get_product_holder) -> double;
  void SetContractValue(const std::string& native_product, double val);
  void ParseFixSecurityList(const FIX::Message& message);

 private:
  void FillLogonMessage(FIX::Message&);

 private:
  const MarketExchangeApi mea_;
  const std::string trade_client_config_ = "data/coin2/order/fix/deribit/tradeclient.cfg";
  const std::string begin_string_ = "FIX.4.4";
  const std::string target_comp_id_ = "DERIBITSERVER";
  std::unique_ptr<FixMessageBuilder> msg_builder_;
  GetProductHolderFunc get_product_holder_;

  std::unique_ptr<boost::asio::steady_timer> timer_ = {};

  std::unordered_map<std::string, double> contract_value_map_;
};  // class FixTradeClient

}  // namespace coin2::exchange::deribit::api_order::fix
